import com.lefu.utils.common.excelhelper.ExcelRead;
import com.lefu.utils.common.excelhelper.ExcelWrite;
import com.lefu.utils.common.excelhelper.Head;
import com.lefu.utils.common.exception.LefuException;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Connection;
import org.jsoup.*;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.*;
import java.util.*;

/**
 * 爬子分类，分类下有那些商品
 * Created by lrs on 2018/2/23.
 */
public class SubCategory {
    private String homeURL = ""; // 根网址

    private static Map<String, String> DATAS = null; // 保存 form 表单提交的数据

    /**
     * 构造函数
     * @param homeURL
     */
    public SubCategory(String homeURL) {
        this.homeURL = homeURL;
    }

    /**
     * 第一个page
     * @param url url地址
     * @return
     * @throws IOException
     */
    private Document onePage(String url) throws IOException {
        Connection con = Jsoup.connect(url);
        Connection.Response response = con.method(Connection.Method.GET).execute();

        YXSpider.COOKIES = response.cookies(); // 保存当前 cookies（没有登录所以保存了意义也不大）

        // 得到form表单上的所有input然后保存到dats中以备之后使用
        Document doc = Jsoup.parse(response.body());

        // 如果datas为空则初始化
        if (null == this.DATAS) {
            this.DATAS = new HashMap<String, String>();
            Elements inputElemets = doc.select("form[method=post]").first().select("input[name]");
            for (Iterator it = inputElemets.iterator(); it.hasNext(); ) {
                Element inputElement = (Element) it.next();
                if (!StringUtils.isEmpty(inputElement.attr("name"))) {
                    this.DATAS.put(inputElement.attr("name"), inputElement.attr("value"));
                }
            }
            this.DATAS.put("__EVENTARGUMENT", ""); // 由于第二个参数都是""所以设置默认值
        }
        return doc;
    }

    /**
     * 其它page（post方式分页请求数据）
     * @param url url地址
     * @param eventTarget 翻页中的 eventTarget 参数
     * @return Document对象
     * @throws IOException
     */
    private Document otherPage(String url, String eventTarget) throws IOException {
        this.DATAS.put("__EVENTTARGET", eventTarget);

        Connection con = Jsoup.connect(url);
        con.ignoreContentType(true); //
        con.method(Connection.Method.POST).data(this.DATAS); // 设置 post 数据
        con.cookies(YXSpider.COOKIES); // 设置 cookies

        Connection.Response rs = con.execute();
        Document doc = Jsoup.parse(rs.body());
        return doc;
    }

    /**
     * 遍历页面返回所有产品列表
     * @param doc doc
     * @param excelReader 产品分类数据
     */
    private ExcelWrite getProductList(Document doc, ExcelRead excelReader, ExcelWrite excelWrite) throws LefuException {
        Elements productElements = doc.select("div .p_list01 li"); // 通过选择器得到全部产品分类

        // 遍历所有产品
        for (Element productElement : productElements) {
            Elements product = productElement.select("a");
            if (!StringUtils.isEmpty(productElement.select("a").toString())) {
                ArrayList<String> list = new ArrayList<String>(); // 一个产品
                // 添加分类信息
                list.add(excelReader.getString("一级分类"));
                list.add(excelReader.getString("二级分类"));
                list.add(excelReader.getString("三级分类"));
                list.add(excelReader.getString("SysNo")); // 二级分类ID
                list.add(excelReader.getString("LsTypeSysNo")); // 三级分类ID

                String productURL = homeURL + productElement.select("a").attr("href"); // 产品url
                String productName = productElement.select(".padding5 div").eq(0).text(); // 产品名称
                String productPrice = productElement.select(".padding5 div span").text(); // 产品价格

                Map<String, String> map = URLHelp.urlSplit(productURL); // 解析出url参数中的键值对
                String productID = map.get("SysNo"); // 产品id

                list.add(productID); // 产品id
                list.add(productName); // 产品名称
                list.add(productPrice); // 产品单价
                list.add(productURL); // 产品url

                excelWrite.addBody(list);
            }
        }
        return excelWrite;
    }

    /**
     * 返回总页号数
     * @param doc
     * @return
     */
    private int getTotalPageNo(Document doc) {
        int totalPageNo = 0;
        Elements elements = doc.select("div .pagenation span"); // 通过选择器得到总页号
        List list = StringUtil.subString(elements.eq(0).text(), "(?<=共)(.+?)(?=页)"); // 截取页号总数
        if (list.size() == 1) {
          totalPageNo = Integer.parseInt(list.get(0).toString()); // 转成int型
        }
        return totalPageNo;
    }

    /**
     * 创建子分类excel
     * @return
     * @throws LefuException
     */
    private ExcelWrite createSubCategoryExcel() throws LefuException {
        ExcelWrite excelWrite = new ExcelWrite();
        Head head;
        // 一级分类
        head = new Head(0, "一级分类", 25, null);
        excelWrite.addHead(head);
        // 二级分类
        head = new Head(1, "二级分类", 25, null);
        excelWrite.addHead(head);
        // 三级分类
        head = new Head(2, "三级分类", 25, null);
        excelWrite.addHead(head);
        // 三级分类 ID
        head = new Head(3, "三级分类ID", 12, null);
        excelWrite.addHead(head);
        // 二级分类 URL
        head = new Head(4, "二级分类ID", 12, null);
        excelWrite.addHead(head);
        // 产品ID
        head = new Head(5, "产品ID", 12, null);
        excelWrite.addHead(head);
        // 产品名称
        head = new Head(6, "产品名称", 25, null);
        excelWrite.addHead(head);
        // 产品单价
        head = new Head(7, "产品单价", 10, null);
        excelWrite.addHead(head);
        // 二级分类 URL
        head = new Head(8, "产品URL", 50, null);
        excelWrite.addHead(head);

        return excelWrite;
    }

    /**
     * 读取断点文件
     * @return
     * @throws IOException
     */
    private String[] readBreakpoint() throws IOException {
        InputStreamReader reader = new InputStreamReader(new FileInputStream("./output/breakpoint")); // 建立一个输入流对象reader
        BufferedReader br = new BufferedReader(reader); // 建立一个对象，它把文件内容转成计算机能读懂的语言
        String line = "";
        line = br.readLine();
        br.close();
        reader.close();
        if (!StringUtils.isEmpty(line)) {
            return line.split(",");
        } else {
            return null;
        }
    }

    /**
     * 写入断点文件
     * @param LsTypeSysNo
     * @param SysNo
     * @throws IOException
     */
    private void writeBreakpoint(String LsTypeSysNo, String SysNo) throws IOException {
        File writename = new File("./output/breakpoint"); // 相对路径，如果没有则要建立一个新的output。txt文件
        writename.createNewFile(); // 创建新文件
        BufferedWriter out = new BufferedWriter(new FileWriter(writename));
        out.write("SubCategory," + LsTypeSysNo + "," + SysNo + "\r\n"); // \r\n即为换行
        out.flush(); // 把缓存区内容压入文件
        out.close(); // 最后记得关闭文件
    }

    /**
     * 爬取子分类
     * @throws Exception
     */
    public void spiderSubCategory() throws Exception {
        // 读取断点文件，并且计算上一次失败位置
        String[] breakpoint = readBreakpoint();
        String breakLsTypeSysNo;
        String breakSysNo;
        if (null == breakpoint) {
            breakLsTypeSysNo = "";
            breakSysNo = "";
        } else if (breakpoint[0].equals("SubCategory")) {
            breakLsTypeSysNo = breakpoint[1];
            breakSysNo = breakpoint[2];
        } else {
            return;
        }

        // 读取主分类excel文件
        File file = new File("./output/category.xls");
        FileInputStream fis = new FileInputStream(file);
        ExcelRead excelReader = new ExcelRead(fis);
        while (excelReader.next()) {
            String LsTypeSysNo = excelReader.getString("LsTypeSysNo");
            String SysNo = excelReader.getString("SysNo");

            // 判断如果之前断了则眺到下一个爬取
            if (!StringUtils.isEmpty(breakLsTypeSysNo) || !StringUtils.isEmpty(breakSysNo)) {
                if (!(breakLsTypeSysNo.equals(LsTypeSysNo) && breakSysNo.equals(SysNo))) {
                    continue;
                }
            }
            // 清除断点文件记录变量
            breakLsTypeSysNo = "";
            breakSysNo = "";

            writeBreakpoint(LsTypeSysNo, SysNo); // 将当前爬取记录计入断点文件

            if (!StringUtils.isEmpty(LsTypeSysNo) && !StringUtils.isEmpty(SysNo)) {
                // 创建分类excel
                FileOutputStream fos = new FileOutputStream("./output/subCategory_LsTypeSysNo_" + LsTypeSysNo + "_SysNo_" + SysNo+ ".xls");
                ExcelWrite excelWrite = this.createSubCategoryExcel();

                String url = "http://www.asonline.cn/ProIndexpp.aspx?lstypesysno=" + LsTypeSysNo + "&sysno=" + SysNo;
                System.out.println("==========================================");
                System.out.println("爬取子分类: " + url + " 第1页");

                Document doc = onePage(url); // 每个三级分类第一个页面
                excelWrite = getProductList(doc, excelReader, excelWrite); // 遍历第一页，返回所有产品列表

                // 如果有多个页面遍历其它页
                int totalPageNo = getTotalPageNo(doc); // 从第一个页面得到总页号数
                for (int i = 2; i <= totalPageNo; i++) {
                    System.out.println("爬取子分类: " + url + " 第" + i + "页");
                    doc = otherPage(url, "BestPager1$lbtnPage" + i);
                    excelWrite = getProductList(doc, excelReader, excelWrite); // 遍历其它页，返回所有产品列表
                }

                // 关闭当前子分类的excel
                excelWrite.write(fos);
                fos.close();
            }
        }

        // 标记子分类正常处理完成，开始爬取产品信息
        FileHelp fileHelp = new FileHelp();
        fileHelp.writeFile("./output/breakpoint", "Product");
    }

}
